# -*- coding: binary -*-

require 'rex/proto/sip/response'

module Msf
  # SIP protocol support
  module Exploit::Remote::SIP
    # Parses +response+, extracts useful metdata and then reports on it.
    # Returns true iff the response was a valid SIP response
    def report_response(response, rhost, proto, desired_headers = %w(User-Agent Server Allow))
      endpoint = "#{rhost}:#{rport} #{proto}"
      begin
        options_response = Rex::Proto::SIP::Response.parse(response)
      rescue ArgumentError => e
        vprint_error("#{endpoint} is not SIP: #{e}")
        return false
      end

      # Extracted headers, stored as a hash where the key is the header name
      # and the value is a list of all values seen for the header, covering the
      # case where the same header value is seen multiple times
      extracted_headers = {}
      unless desired_headers.nil? || desired_headers.empty?
        desired_headers.each do |desired_header|
          next unless (found_header = options_response.header(desired_header))
          extracted_headers[desired_header] ||= []
          extracted_headers[desired_header]  |= found_header
        end
      end

      # Create a SIP OPTIONS fingerprint hash
      fprint = {
        'code'    => options_response.code,
        'message' => options_response.message
      }

      # compact the header values, append the header information to the
      # fingerprint hash
      extracted_headers.each_pair do |k,v|
        value = v.join(',')
        extracted_headers[k] = value
        fprint['header_' + k.gsub('-', '_').downcase] = value
      end

      # Create a summary of the response
      status = options_response.status_line.dup
      unless extracted_headers.keys.empty?
        status << ": #{extracted_headers}"
      end

      # Report the service with the status information
      report_service(
        host: rhost,
        port: rport,
        proto: proto.downcase,
        name: 'sip',
        info: status
      )

      # Report the fingerprint information
      report_note(
        host: rhost,
        port: rport,
        proto: proto.downcase,
        type: "sip.options.fingerprint",
        data: fprint
      )

      # Display the actual result to the user
      print_status(endpoint + " " + status)
      true
    end

    def create_probe(ip, proto)
      suser = Rex::Text.rand_text_alphanumeric(rand(8) + 1)
      shost = Rex::Socket.source_address(ip)
      src   = "#{shost}:#{datastore['RPORT']}"

      data  = "OPTIONS sip:#{datastore['TO']}@#{ip} SIP/2.0\r\n"
      data << "Via: SIP/2.0/#{proto.upcase} #{src};branch=z9hG4bK.#{format('%.8x', rand(0x100000000))};rport;alias\r\n"
      data << "From: sip:#{suser}@#{src};tag=70c00e8c\r\n"
      data << "To: sip:#{datastore['TO']}@#{ip}\r\n"
      data << "Call-ID: #{rand(0x100000000)}@#{shost}\r\n"
      data << "CSeq: 1 OPTIONS\r\n"
      data << "Contact:  sip:#{suser}@#{src}\r\n"
      data << "Max-Forwards: 20\r\n"
      data << "User-Agent: #{suser}\r\n"
      data << "Accept: application/sdp\r\n"
      data << "Content-Length: 0\r\n"
      data << "\r\n"
      data
    end
  end
end
